#include <Kharlia/Engine/Common.h>
#include <Kharlia/Engine/Init.h>
#include <Kharlia/Engine/Engine.h>

#include <CEGUIInputEvent.h>

namespace kharlia { namespace engine {

#define MK(NAME, VALUE) {ui::Key::VALUE, 0, #NAME},

namespace {

struct FKeyInfo {
    ui::Key::Scan UICode;
    int WXCode; // in case we implement this in the future
    char const* Name;
};

// Map CEGUI keys to be exposed to python
// This matches the order as declared in CEGUIInputEvent.h
// Since not all of them are mapped yet, some spaces will be left
FKeyInfo GKeyMap[] = {
    MK(ARROW_LEFT, ArrowLeft)
    MK(ARROW_RIGHT, ArrowRight)
    MK(ARROW_UP, ArrowUp)
    MK(ARROW_DOWN, ArrowDown)
    // In order from here
    MK(ESCAPE, Escape)
    MK(ONE, One)
    MK(TWO, Two)
    MK(THREE, Three)
    MK(FOUR, Four)
    MK(FIVE, Five)
    MK(SIX, Six)
    MK(SEVEN, Seven)
    MK(EIGHT, Eight)
    MK(NINE, Nine)
    MK(ZERO, Zero)
    MK(MINUS, Minus)
    MK(EQUALS, Equals)
    MK(BACKSPACE, Backspace)
    MK(TAB, Tab)
    MK(Q, Q)
    MK(W, W)
    MK(E, E)
    MK(R, R)
    MK(T, T)
    MK(Y, Y)
    MK(U, U)
    MK(I, I)
    MK(O, O)
    MK(P, P)
    MK(LEFT_BRACKET, LeftBracket)
    MK(RIGHT_BRACKET, RightBracket)
    MK(RETURN, Return)
    MK(LEFT_CONTROL, LeftControl)
    MK(A, A)
    MK(S, S)
    MK(D, D)
    MK(F, F)
    MK(G, G)
    MK(H, H)
    MK(J, J)
    MK(K, K)
    MK(L, L)
    MK(SEMICOLON, Semicolon)
    MK(APOSTROPHE, Apostrophe)
    MK(GRAVE, Grave)
    MK(LEFT_SHIFT, LeftShift)
    MK(BACKSLASH, Backslash)
    MK(Z, Z)
    MK(X, X)
    MK(C, C)
    MK(V, V)
    MK(B, B)
    MK(N, N)
    MK(M, M)
    MK(COMMA, Comma)
    MK(PERIOD, Period)
    MK(SLASH, Slash)
    MK(RIGHT_SHIFT, RightShift)
    //MK(
    //MK(
    MK(SPACE, Space)
    //MK(
    MK(F1, F1)
    MK(F2, F2)
    MK(F3, F3)
    MK(F4, F4)
    MK(F5, F5)
    MK(F6, F6)
    MK(F7, F7)
    MK(F8, F8)
    MK(F9, F9)
    MK(F10, F10)
    // ...
    MK(F11, F11)
    MK(F12, F12)
    // ...
    MK(DELETE, Delete)
    {ui::Key::Escape, 0, nullptr},
};

} // namespace

void _InitEngineBindings() {
    enum_<ui::MouseButton>("EMouseButton")
        .value("LEFT", ui::LeftButton)
        .value("RIGHT", ui::RightButton)
        .value("MIDDLE", ui::MiddleButton)
        //.export_values()
        ;

    {
        enum_<ui::Key::Scan> keyenum("EKey");
    
        for (FKeyInfo* k = GKeyMap; k->Name != nullptr; k++) {
            keyenum.value(k->Name, k->UICode);
        }

        //keyenum.export_values();
    }
        
    class_<CEngine, bases<core::CSubsystem>, boost::noncopyable>
        ("CEngineBase", init<>())
        .def("Init", &CEngine::InitImpl)
        .def("Exit", &CEngine::ExitImpl)
        .def("InitClient", &CEngine::InitClient)
        .def("ExitClient", &CEngine::ExitClient)
        .def("TaskletMain", &CEngine::TaskletMain)
        .def("Tick", &CEngine::Tick)
        .def("Exec", &CEngine::ExecImpl)
        .def("Connect", &CEngine::Connect)
        .add_property("World", make_function(&CEngine::GetWorld, return_value_policy<return_by_value>()), &CEngine::SetWorld)
        .def_readwrite("Config", &CEngine::mConfig)
        .def_readonly("LocalPlayer", &CEngine::mLocalPlayer)
        .def_readwrite("ClientTickRate", &CEngine::mClientTickRate)
        .def_readonly("Client", &CEngine::mClient)
        .def_readonly("ResourceManager", &CEngine::mResourceManager)
        //.def("GetInstance", &CEngine::GetInstance).staticmethod("GetInstance")
        // EDITOR
        .add_property("EditorGridVisible", &CEngine::GetEditorGridVisible, &CEngine::SetEditorGridVisible)
        .add_property("EditorEnabled", &CEngine::GetEditorEnabled, &CEngine::SetEditorEnabled)
        .add_property("EditorBrush", make_getter(&CEngine::mEditorBrush), &CEngine::SetEditorBrush)
        .add_property("EditorBrushVisible", &CEngine::GetEditorBrushVisible, &CEngine::SetEditorBrushVisible)
        .add_property("EditorBrushPos", make_function(&CEngine::GetEditorBrushPos, return_internal_reference<>()), &CEngine::SetEditorBrushPos)
        .add_property("_EditorSelection", make_function(&CEngine::GetEditorSelection, return_internal_reference<>()), &CEngine::SetEditorSelection)
        .add_property("EditorSelectionVisible", &CEngine::GetEditorSelectionVisible, &CEngine::SetEditorSelectionVisible)
        ;

    def("GetEngine", &GetEngine);

    KH_IMPL_GC(CEngine);
    RegisterPtrConverter<CEngine>();
}

}}